#include <stdio.h>
#include <stdlib.h>
#include "Hw3Types.h"
#include "Hw3TypesExtended.h"
#include "GlobalVariables.h"
#include "SyncAndProtectionModule.h"
#include "Output.h"

BOOL IsProgramTerminated()
{
	// check the status of the hTerminateProgram event. if it is set, return TRUE.
	DWORD WaitResult;
	WaitResult = WaitForSingleObject(hTerminateProgram,0);
	return (WaitResult == WAIT_OBJECT_0);
}

BOOL CreateReadEvents()
{
	//create an event for each line in the picture 
	int i;
	for (i=0 ; i < ImageReaderAndProperties.Height ; i++)
	{
		hReadEvents[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (NULL == hReadEvents[i])
		{
			PrintErrorAndTerminate(ERROR_CREATING_EVENT);
			return FALSE;
		}
	}
	return TRUE;
}

void CloseReadEvents()
{
	// close all read event handles
	int i;
	for (i=0 ; i < ImageReaderAndProperties.Height ; i++)
	{
		if (hReadEvents[i] != NULL)
			CloseHandle(hReadEvents[i]);
	}
}


BOOL IncreaseOpenThreadsCounter()
{
	// wait for access to the counter
	DWORD WaitRes = WaitForSingleObject(OpenThreadsCounterMutex, INFINITE);
	if (WaitRes == WAIT_OBJECT_0)
	{
		// critical section
		OpenThreadsCounter++;
		#ifdef DEBUG_MODE
			printf("Open threads counter was increased. values is now: %d\n", OpenThreadsCounter);
		#endif
		// end of critical section

	}
	else
	{
		return FALSE;
		//error handling
	}
	ReleaseMutex(OpenThreadsCounterMutex);

	return TRUE;
}

BOOL DecreaseOpenThreadsCounter()
{
	// wait for access to the counter
	DWORD WaitRes = WaitForSingleObject(OpenThreadsCounterMutex, INFINITE);
	if (WaitRes == WAIT_OBJECT_0)
	{
		// critical section
		OpenThreadsCounter--;

		#ifdef DEBUG_MODE
			printf("Open threads counter was decreased. values is now: %d\n", OpenThreadsCounter);
		#endif

		// end of critical section
	}
	else
	{
		return FALSE;
		//error handling
	}
	ReleaseMutex(OpenThreadsCounterMutex);
	return TRUE;
}

void PrintErrorAndTerminate(TError Error)
{
	// print the appropriate error to the end file in case of error
		switch (Error)	
	{
		case ERROR_ALLOCATING_MEMORY:
			WriteEndFile("Error allocating memory\n");
			break;
		case ERROR_CREATING_MUTEX:
			WriteEndFile("Error creating mutex\n");
			break;
		case ERROR_CREATING_THREAD:
			WriteEndFile("Error creating thread\n");
			break;
		case ERROR_CREATING_SEMAPHORE:
			WriteEndFile("Error creating semaphore\n");
			break;
		case ERROR_RELEASING_SEMAPHORE:
			WriteEndFile("Error releasing semaphore\n");
			break;
		case ERROR_RELEASING_MUTEX:
			WriteEndFile("Error releasing mutex\n");
			break;
		case ERROR_WAITING_FOR_SINGLE_OBJECT:
			WriteEndFile("Error waiting for single object\n");
			break;
		case ERROR_SETTING_EVENT:
			WriteEndFile("Error setting event\n");
			break;
		case ERROR_WAITING_FOR_MULTIPLE_OBJECT:
			WriteEndFile("Error waiting for multiple object\n");
			break;
		case ERROR_READING_ROW:
			WriteEndFile("Error reading row\n");
			break;
		case ERROR_INITIALIZING_READER:
			WriteEndFile("Error initializing reader\n");
			break;
		case ERROR_ADDING_FACE_TO_FACEBOOK:
			WriteEndFile("Error adding face to face book\n");
			break;
		case ERROR_CREATING_EVENT:
			WriteEndFile("Error creating event\n");
			break;
		case ERROR_GETTING_IMAGE_PROPERTIES:
			WriteEndFile("Error getting image properties\n");
			break;
		case ERROR_WRITING_TO_FILE:
			WriteEndFile("Error writing to file\n");
			break;
		default:
			WriteEndFile("General error\n");
	}
	SetEvent(hTerminateProgram);
}


/**
 * This function creates a thread by calling Win32 Api's CreateThread()
 * function, and setting some of the parameters to default value. 
 *
 * parameters:
 * ----------
 * StartAddress - a pointer to the function that will be run by the thread.
 * ParameterPtr - a pointer to the parameter that will be supplied to the 
 *                function run by the thread.
 * ThreadIdPtr - return argument: a pointer to a DWORD variable into which 
 *               the function will write the created thread's ID.
 *
 * returns:
 * --------
 * On success, a handle to the created thread. On Failure - NULL.
 */
HANDLE CreateThreadSimple(  LPTHREAD_START_ROUTINE StartAddress, 
                            LPVOID ParameterPtr, 
                            LPDWORD ThreadIdPtr )
{
    return CreateThread(
                        NULL,            /*  default security attributes */
                        0,                /*  use default stack size */
                        StartAddress,    /*  thread function */
                        ParameterPtr,    /*  argument to thread function */
                        0,                /*  use default creation flags */
                        ThreadIdPtr );    /*  returns the thread identifier */
}